package mspbots.cw.business.core.users;

import com.fasterxml.jackson.databind.JsonNode;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import mspbots.cw.business.core.BaseReceiver;
import mspbots.cw.business.core.sysuser.SysUser;
import mspbots.cw.business.core.sysuser.SysUserMapping;
import mspbots.cw.business.core.sysuser.SysUserMappingRepository;
import mspbots.cw.business.core.sysuser.SysUserRepository;
import mspbots.cw.business.core.teams.Teams;
import mspbots.cw.common.ResultMsg;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.data.relational.core.query.Criteria;
import org.springframework.data.relational.core.query.Query;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author Jay.Yang
 */
@Log4j2
@Component
@RequiredArgsConstructor
public class UsersReceiver extends BaseReceiver {

    private final UsersRepository usersRepository;
    private final SysUserRepository sysUserRepository;
    private final SysUserMappingRepository sysUserMappingRepository;
    private final UserTeamMappingRepository userTeamMappingRepository;

    @RabbitListener(queues = "connect-wise.users.tl")
    public void process(ResultMsg resultMsg) {
        JsonNode entity = resultMsg.getEntity();
        Users data = super.objectMapper.convertValue(entity, Users.class);
        data.setId(null);
        data.setCwUid(resultMsg.getId());
        data.setTenantCode(resultMsg.getTenantId());
        data.setSyncTime(resultMsg.getSyncDate());

        data.setDepartmentId(super.getLongValueId(entity.get("defaultDepartment")));
        data.setEmail(super.getStringValue(entity.get("officeEmail")));
        data.setTenantRoleId(super.getLongValueId(entity.get("securityRole")));
        data.setTenantRoleName(super.getStringValueName(entity.get("securityRole")));
        data.setUpdateTime(super.getDateTimeValue(entity.findPath("lastUpdated")));
        data.setReportsToName(super.getStringValueName(entity.get("reportsTo")));
        data.setSecurityLocationName(super.getStringValueName(entity.get("securityLocation")));
        data.setTypeName(super.getStringValueName(entity.get("type")));
        data.setServiceDefaultDepartmentName(super.getStringValueName(entity.get("serviceDefaultDepartment")));
        data.setWorkTypeName(super.getStringValueName(entity.get("workType")));
        data.setServiceLocationName(super.getStringValueName(entity.get("serviceLocation")));
        data.setSecurityRoleName(super.getStringValueName(entity.get("securityRole")));
        data.setHireDate(super.getDateTimeValue(entity.get("hireDate")));
        data.setCreateDate(LocalDateTime.now());

        Users data1 = this.usersRepository.findByCwUidAndTenantCode(resultMsg.getId(), resultMsg.getTenantId())
                .map(result -> {
                    data.setId(result.getId());
                    data.setCreateDate(result.getCreateDate());
                    data.setUpdateDate(LocalDateTime.now());
                    return data;
                })
                .defaultIfEmpty(data)
                .flatMap(this.usersRepository::save).block(Duration.ofSeconds(10));

        Long sysUserId = processMapping(data);
        processTeams(entity, data1, sysUserId);
    }

    private Long processMapping(Users data) {
        Long sysUserId = null;
        SysUserMapping userMapping = this.sysUserMappingRepository
                .findByMappingUserIdAndUserTypeAndTenantCode(data.getCwUid().toString(), SysUserMapping.USER_TYPE_CW, data.getTenantCode()).block(Duration.ofSeconds(10));

        if (ObjectUtils.isEmpty(userMapping)) {
            if (StringUtils.isEmpty(data.getEmail())) {
                return null;
            }
            SysUser sysUser = this.sysUserRepository.findByEmailAndTenantCode(data.getEmail().toLowerCase(), data.getTenantCode()).block(Duration.ofSeconds(10));
            if (ObjectUtils.isEmpty(sysUser)) {
                return null;
            }
            sysUserId = sysUser.getId();
            if (!ObjectUtils.isEmpty(data.getInactiveFlag())
                    && !data.getInactiveFlag()
                    && !"A".equalsIgnoreCase(data.getLicenseClass())) {
                SysUserMapping sysUserMapping = SysUserMapping.builder()
                        .userId(sysUser.getId())
                        .mappingUserId(data.getCwUid().toString())
                        .userType(SysUserMapping.USER_TYPE_CW).build();
                sysUserMapping.setTenantCode(data.getTenantCode());
                sysUserMapping.setCreateDate(LocalDateTime.now());
                this.sysUserMappingRepository.save(sysUserMapping).block(Duration.ofSeconds(10));
            }
        } else if (data.getInactiveFlag() || "A".equalsIgnoreCase(data.getLicenseClass())) {
            this.sysUserMappingRepository.deleteById(userMapping.getId()).block(Duration.ofSeconds(10));
        }
        return sysUserId;
    }

    private void processTeams(JsonNode entity, Users data1, Long sysUserId) {
        if (ObjectUtils.isEmpty(entity.get("serviceBoardTeamIds")) || entity.get("serviceBoardTeamIds").isEmpty()) {
            return;
        }
        Iterator<JsonNode> ids = entity.get("serviceBoardTeamIds").elements();
        List<Long> idList = new ArrayList<>();
        while (ids.hasNext()) {
            idList.add(ids.next().asLong());
        }
        List<UserTeamMapping> mappingList = userTeamMappingRepository.findByUserIdAndTenantCode(data1.getId(),
                data1.getTenantCode()).collectList().block(Duration.ofSeconds(10));

        List<Long> teamIds = this.r2dbcEntityTemplate.select(Teams.class)
                .matching(Query.query(Criteria.where("tenantCode").is(data1.getTenantCode())
                        .and("teamId").in(idList))).all().map(Teams::getId)
                .collectList().block(Duration.ofSeconds(10));

        List<UserTeamMapping> saveMappingList = new ArrayList<>();
        List<Long> mappingTeamIdList = new ArrayList<>();
        if (!CollectionUtils.isEmpty(mappingList)) {
            mappingTeamIdList = mappingList.stream().map(UserTeamMapping::getTeamId).collect(Collectors.toList());
        }
        for (Long id : teamIds) {
            if (mappingTeamIdList.contains(id)) {
                continue;
            }
            UserTeamMapping m = UserTeamMapping.builder()
                    .teamId(id)
                    .userId(data1.getId())
                    .createDate(LocalDateTime.now())
                    .sysUserId(sysUserId)
                    .build();
            m.setTenantCode(data1.getTenantCode());
            saveMappingList.add(m);
        }
        if (!CollectionUtils.isEmpty(saveMappingList)) {
            userTeamMappingRepository.saveAll(saveMappingList)
                    .collectList().block(Duration.ofSeconds(60));
        }
    }

}
